package model

import (
	"my_framework/orm/internal/errs"
	"reflect"
	"strings"
	"sync"
	"unicode"
)

const (
	tagKeyColumn = "column"
)

type Model struct {
	TableName string
	// 为了保证顺序遍历
	Fields []*Field
	// 字段名-字段定义
	FieldMap map[string]*Field
	// 列名-字段定义
	ColumnMap map[string]*Field
}

type Field struct {
	// 字段名
	GoName string
	// 列名
	ColName string
	// 列的类型
	Typ reflect.Type
	// 字段偏移量(相对于结构体)
	Offset uintptr
}

// 全局默认的
//var defaultRegistry = &registry{
//	models: map[reflect.Type]*Model{},
//}

// 元数据注册中心
type registry struct {
	// 读写锁
	//lock   sync.RWMutex
	//models map[reflect.Type]*Model
	models sync.Map
}

// func NewRegistry() *registry {
func NewRegistry() Registry {
	return &registry{
		//models: make(map[reflect.Type]*Model, 64),
	}
}

func (r *registry) Get(val any) (*Model, error) {
	typ := reflect.TypeOf(val)
	m, ok := r.models.Load(typ)
	if ok {
		return m.(*Model), nil
	}
	m, err := r.Registry(val)
	if err != nil {
		return nil, err
	}
	return m.(*Model), nil
}

//func (r *registry) get1(val any) (*Model, error) {
//	typ := reflect.TypeOf(val)
//
//	r.lock.RLock()
//	m, ok := r.models[typ]
//	r.lock.RUnlock()
//
//	if ok {
//		return m, nil
//	}
//
//	r.lock.Lock()
//	defer r.lock.Unlock()
//	m, ok = r.models[typ]
//	if ok {
//		return m, nil
//	}
//	var err error
//	m, err = r.Register(val)
//	if err != nil {
//		return nil, err
//	}
//	r.models[typ] = m
//	return m, nil
//}

// 解析创建model元数据
func (r *registry) Registry(entity any, opts ...ModelOpt) (*Model, error) {
	typ := reflect.TypeOf(entity)
	if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct {
		return nil, errs.ErrPointerOnly
	}
	// 指针
	// 可以处理多级指针
	//for elemType.Kind() == reflect.Pointer {
	// 只能处理一级指针
	//if typ.Kind() == reflect.Pointer {
	elemType := typ.Elem()
	//}

	numField := elemType.NumField()

	fieldMap := make(map[string]*Field, numField)
	columnMap := make(map[string]*Field, numField)
	fields := make([]*Field, 0, numField)
	for i := 0; i < numField; i++ {
		fd := elemType.Field(i)
		// 取tag `xxx:xxx=xxx`
		pair, err := r.parseTag(fd.Tag)
		if err != nil {
			return nil, err
		}
		// 取column: `orm:"column"=xxx`
		colName := pair[tagKeyColumn]
		if colName == "" {
			// 用户没有设置,用字段名
			colName = underscoreName(fd.Name)
		}
		fdData := &Field{
			GoName: fd.Name,
			//colName: underscoreName(fd.Name),
			ColName: colName,
			Typ:     fd.Type,
			Offset:  fd.Offset,
		}
		fieldMap[fd.Name] = fdData
		columnMap[colName] = fdData
		fields = append(fields, fdData)
	}

	var tableName string
	if tbl, ok := entity.(TableName); ok {
		tableName = tbl.TableName()
	}
	if tableName == "" {
		tableName = underscoreName(elemType.Name())
	}

	res := &Model{
		TableName: tableName,
		FieldMap:  fieldMap,
		ColumnMap: columnMap,
		Fields:    fields,
	}

	for _, opt := range opts {
		err := opt(res)
		if err != nil {
			return nil, err
		}
	}

	r.models.Store(typ, res)
	return res, nil
}

type User struct {
	ID uint64 `orm:"column=id,xxx=bbb"`
}

func (r *registry) parseTag(tag reflect.StructTag) (map[string]string, error) {
	ormTag, ok := tag.Lookup("orm")
	if !ok {
		return map[string]string{}, nil
	}
	// orm:"xxx=xxx,xxx=xxx"
	pairs := strings.Split(ormTag, ",")
	res := make(map[string]string, len(pairs))
	for _, pair := range pairs {
		segs := strings.Split(pair, "=")
		if len(segs) != 2 {
			return nil, errs.NewErrInvalidTagContent(pair)
		}
		key := segs[0]
		val := segs[1]
		res[key] = val
	}
	return res, nil
}

// underscoreName 驼峰命名转下划线
func underscoreName(tableName string) string {
	var buf []byte
	for i, v := range tableName {
		if unicode.IsUpper(v) {
			if i != 0 {
				buf = append(buf, '_')
			}
			buf = append(buf, byte(unicode.ToLower(v)))
		} else {
			buf = append(buf, byte(v))
		}
	}
	return string(buf)
}

type Registry interface {
	Get(val any) (*Model, error)
	Registry(val any, opts ...ModelOpt) (*Model, error)
}

type ModelOpt func(m *Model) error

func WithTableName(tableName string) ModelOpt {
	return func(m *Model) error {
		m.TableName = tableName
		//if tableName==""{
		//	return err
		//}
		return nil
	}
}

func WithColumnName(field string, colName string) ModelOpt {
	return func(m *Model) error {
		fd, ok := m.FieldMap[field]
		if !ok {
			return errs.NewErrUnknownField(field)
		}
		fd.ColName = colName
		return nil
	}
}

type TableName interface {
	TableName() string
}
